{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# CHAPTER 1 The Python Data Model\n",
    "##  A Pythonic Card Deck\n",
    "You can think of the Data Model as a description of Python as a framework. It formalizes\n",
    "the interfaces of the building blocks of the language itself, such as sequences, iterators,\n",
    "functions, classes, context managers and so on.\n",
    "\n",
    "The special method names are always spelled\n",
    "with leading and trailing double underscores, i.e. __getitem__."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import collections\n",
    "Card = collections.namedtuple('Card', ['rank', 'suit'])\n",
    "class FrenchDeck:\n",
    "    ranks = [str(n) for n in range(2, 11)] + list('JQKA')\n",
    "    suits = 'spades diamonds clubs hearts'.split()\n",
    "    def __init__(self):\n",
    "        self._cards = [Card(rank, suit) for suit in self.suits\n",
    "            for rank in self.ranks]\n",
    "    def __len__(self):\n",
    "        return len(self._cards)\n",
    "    def __getitem__(self, position):\n",
    "        return self._cards[position]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  collections.namedtuple(typename, field_names, *, verbose=False, rename=False, module=None)\n",
    "Returns a new tuple subclass named typename. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with typename and field_names) and a helpful __repr__() method which lists the tuple contents in a name=value format.\n",
    "\n",
    "The field_names are a sequence of strings such as ['x', 'y']. Alternatively, field_names can be a single string with each fieldname separated by whitespace and/or commas, for example 'x y' or 'x, y'."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### str.split(sep=None, maxsplit=-1)\n",
    "Return a list of the words in the string, using sep as the delimiter string. If maxsplit is given, at most maxsplit splits are done (thus, the list will have at most maxsplit+1 elements). If maxsplit is not specified or -1, then there is no limit on the number of splits (all possible splits are made).\n",
    "\n",
    "If sep is given, consecutive delimiters are not grouped together and are deemed to delimit empty strings (for example, '1,,2'.split(',') returns ['1', '', '2']). The sep argument may consist of multiple characters (for example, '1<>2<>3'.split('<>') returns ['1', '2', '3']). Splitting an empty string with a specified separator returns ['']."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='7', suit='diamonds')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "beer_card=Card('7','diamonds')\n",
    "beer_card"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "52\n",
      "Card(rank='2', suit='spades')\n",
      "Card(rank='A', suit='hearts')\n",
      "------\n",
      "Card(rank='2', suit='spades')\n",
      "Card(rank='3', suit='spades')\n",
      "Card(rank='4', suit='spades')\n",
      "Card(rank='5', suit='spades')\n",
      "Card(rank='6', suit='spades')\n",
      "Card(rank='7', suit='spades')\n",
      "Card(rank='8', suit='spades')\n",
      "Card(rank='9', suit='spades')\n",
      "Card(rank='10', suit='spades')\n",
      "Card(rank='J', suit='spades')\n",
      "Card(rank='Q', suit='spades')\n",
      "Card(rank='K', suit='spades')\n",
      "Card(rank='A', suit='spades')\n",
      "Card(rank='2', suit='diamonds')\n",
      "Card(rank='3', suit='diamonds')\n",
      "Card(rank='4', suit='diamonds')\n",
      "Card(rank='5', suit='diamonds')\n",
      "Card(rank='6', suit='diamonds')\n",
      "Card(rank='7', suit='diamonds')\n",
      "Card(rank='8', suit='diamonds')\n",
      "Card(rank='9', suit='diamonds')\n",
      "Card(rank='10', suit='diamonds')\n",
      "Card(rank='J', suit='diamonds')\n",
      "Card(rank='Q', suit='diamonds')\n",
      "Card(rank='K', suit='diamonds')\n",
      "Card(rank='A', suit='diamonds')\n",
      "Card(rank='2', suit='clubs')\n",
      "Card(rank='3', suit='clubs')\n",
      "Card(rank='4', suit='clubs')\n",
      "Card(rank='5', suit='clubs')\n",
      "Card(rank='6', suit='clubs')\n",
      "Card(rank='7', suit='clubs')\n",
      "Card(rank='8', suit='clubs')\n",
      "Card(rank='9', suit='clubs')\n",
      "Card(rank='10', suit='clubs')\n",
      "Card(rank='J', suit='clubs')\n",
      "Card(rank='Q', suit='clubs')\n",
      "Card(rank='K', suit='clubs')\n",
      "Card(rank='A', suit='clubs')\n",
      "Card(rank='2', suit='hearts')\n",
      "Card(rank='3', suit='hearts')\n",
      "Card(rank='4', suit='hearts')\n",
      "Card(rank='5', suit='hearts')\n",
      "Card(rank='6', suit='hearts')\n",
      "Card(rank='7', suit='hearts')\n",
      "Card(rank='8', suit='hearts')\n",
      "Card(rank='9', suit='hearts')\n",
      "Card(rank='10', suit='hearts')\n",
      "Card(rank='J', suit='hearts')\n",
      "Card(rank='Q', suit='hearts')\n",
      "Card(rank='K', suit='hearts')\n",
      "Card(rank='A', suit='hearts')\n",
      "------------\n",
      "Card(rank='2', suit='spades')\n",
      "Card(rank='3', suit='spades')\n",
      "Card(rank='4', suit='spades')\n",
      "Card(rank='5', suit='spades')\n",
      "Card(rank='6', suit='spades')\n",
      "Card(rank='7', suit='spades')\n",
      "Card(rank='8', suit='spades')\n",
      "Card(rank='9', suit='spades')\n",
      "Card(rank='10', suit='spades')\n",
      "Card(rank='J', suit='spades')\n",
      "Card(rank='Q', suit='spades')\n",
      "Card(rank='K', suit='spades')\n",
      "Card(rank='A', suit='spades')\n",
      "Card(rank='2', suit='diamonds')\n",
      "Card(rank='3', suit='diamonds')\n",
      "Card(rank='4', suit='diamonds')\n",
      "Card(rank='5', suit='diamonds')\n",
      "Card(rank='6', suit='diamonds')\n",
      "Card(rank='7', suit='diamonds')\n",
      "Card(rank='8', suit='diamonds')\n",
      "Card(rank='9', suit='diamonds')\n",
      "Card(rank='10', suit='diamonds')\n",
      "Card(rank='J', suit='diamonds')\n",
      "Card(rank='Q', suit='diamonds')\n",
      "Card(rank='K', suit='diamonds')\n",
      "Card(rank='A', suit='diamonds')\n",
      "Card(rank='2', suit='clubs')\n",
      "Card(rank='3', suit='clubs')\n",
      "Card(rank='4', suit='clubs')\n",
      "Card(rank='5', suit='clubs')\n",
      "Card(rank='6', suit='clubs')\n",
      "Card(rank='7', suit='clubs')\n",
      "Card(rank='8', suit='clubs')\n",
      "Card(rank='9', suit='clubs')\n",
      "Card(rank='10', suit='clubs')\n",
      "Card(rank='J', suit='clubs')\n",
      "Card(rank='Q', suit='clubs')\n",
      "Card(rank='K', suit='clubs')\n",
      "Card(rank='A', suit='clubs')\n",
      "Card(rank='2', suit='hearts')\n",
      "Card(rank='3', suit='hearts')\n",
      "Card(rank='4', suit='hearts')\n",
      "Card(rank='5', suit='hearts')\n",
      "Card(rank='6', suit='hearts')\n",
      "Card(rank='7', suit='hearts')\n",
      "Card(rank='8', suit='hearts')\n",
      "Card(rank='9', suit='hearts')\n",
      "Card(rank='10', suit='hearts')\n",
      "Card(rank='J', suit='hearts')\n",
      "Card(rank='Q', suit='hearts')\n",
      "Card(rank='K', suit='hearts')\n",
      "Card(rank='A', suit='hearts')\n"
     ]
    }
   ],
   "source": [
    "deck=FrenchDeck()\n",
    "print(len(deck))\n",
    "print(deck[0])\n",
    "print(deck[-1])\n",
    "print('------')\n",
    "for i in range(0,len(deck)):\n",
    "    # print(deck.__getitem__(i))  : the result is the same as below,and this is what the __getitem__ method provides.\n",
    "    print(deck[i])\n",
    "print (\"------------\")   \n",
    "for card in deck :\n",
    "    print (card )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python already has a\n",
    "function to get a random item from a sequence: random.choice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='4', suit='hearts')"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from random import choice\n",
    "\n",
    "choice(deck)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Card(rank='2', suit='spades'),\n",
       " Card(rank='3', suit='spades'),\n",
       " Card(rank='4', suit='spades')]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[:3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Card(rank='A', suit='spades'),\n",
       " Card(rank='A', suit='diamonds'),\n",
       " Card(rank='A', suit='clubs'),\n",
       " Card(rank='A', suit='hearts')]"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[12::13]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### class slice(start, stop[, step])\n",
    "Return a slice object representing the set of indices specified by range(start, stop, step). The start and step arguments default to None. Slice objects have read-only data attributes start, stop and step which merely return the argument values (or their default). They have no other explicit functionality; however they are used by Numerical Python and other third party extensions. Slice objects are also generated when extended indexing syntax is used. For example: a[start:stop:step] or a[start:stop, i]. See itertools.islice() for an alternate version that returns an iterator."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We’ve just seen two advantages of using special methods to leverage the Python Data Model:\n",
    "1. The users of your classes don’t have to memorize arbitrary method names for standard operations (“How to get the number of items? Is it .size() .length() or what?”)\n",
    "2. It’s easier to benefit from the rich Python standard library and avoid reinventing the wheel, like the random.choice function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Card('Q', 'hearts') in deck"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Card('Q', 'bee') in deck"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)\n",
    "def spades_high(card):\n",
    "    rank_value = FrenchDeck.ranks.index(card.rank)\n",
    "    return rank_value * len(suit_values) + suit_values[card.suit]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Card(rank='2', suit='spades')\n",
      "3\n",
      "Card(rank='3', suit='spades')\n",
      "7\n",
      "Card(rank='4', suit='spades')\n",
      "11\n",
      "Card(rank='5', suit='spades')\n",
      "15\n",
      "Card(rank='6', suit='spades')\n",
      "19\n",
      "Card(rank='7', suit='spades')\n",
      "23\n",
      "Card(rank='8', suit='spades')\n",
      "27\n",
      "Card(rank='9', suit='spades')\n",
      "31\n",
      "Card(rank='10', suit='spades')\n",
      "35\n",
      "Card(rank='J', suit='spades')\n",
      "39\n",
      "Card(rank='Q', suit='spades')\n",
      "43\n",
      "Card(rank='K', suit='spades')\n",
      "47\n",
      "Card(rank='A', suit='spades')\n",
      "51\n",
      "Card(rank='2', suit='diamonds')\n",
      "1\n",
      "Card(rank='3', suit='diamonds')\n",
      "5\n",
      "Card(rank='4', suit='diamonds')\n",
      "9\n",
      "Card(rank='5', suit='diamonds')\n",
      "13\n",
      "Card(rank='6', suit='diamonds')\n",
      "17\n",
      "Card(rank='7', suit='diamonds')\n",
      "21\n",
      "Card(rank='8', suit='diamonds')\n",
      "25\n",
      "Card(rank='9', suit='diamonds')\n",
      "29\n",
      "Card(rank='10', suit='diamonds')\n",
      "33\n",
      "Card(rank='J', suit='diamonds')\n",
      "37\n",
      "Card(rank='Q', suit='diamonds')\n",
      "41\n",
      "Card(rank='K', suit='diamonds')\n",
      "45\n",
      "Card(rank='A', suit='diamonds')\n",
      "49\n",
      "Card(rank='2', suit='clubs')\n",
      "0\n",
      "Card(rank='3', suit='clubs')\n",
      "4\n",
      "Card(rank='4', suit='clubs')\n",
      "8\n",
      "Card(rank='5', suit='clubs')\n",
      "12\n",
      "Card(rank='6', suit='clubs')\n",
      "16\n",
      "Card(rank='7', suit='clubs')\n",
      "20\n",
      "Card(rank='8', suit='clubs')\n",
      "24\n",
      "Card(rank='9', suit='clubs')\n",
      "28\n",
      "Card(rank='10', suit='clubs')\n",
      "32\n",
      "Card(rank='J', suit='clubs')\n",
      "36\n",
      "Card(rank='Q', suit='clubs')\n",
      "40\n",
      "Card(rank='K', suit='clubs')\n",
      "44\n",
      "Card(rank='A', suit='clubs')\n",
      "48\n",
      "Card(rank='2', suit='hearts')\n",
      "2\n",
      "Card(rank='3', suit='hearts')\n",
      "6\n",
      "Card(rank='4', suit='hearts')\n",
      "10\n",
      "Card(rank='5', suit='hearts')\n",
      "14\n",
      "Card(rank='6', suit='hearts')\n",
      "18\n",
      "Card(rank='7', suit='hearts')\n",
      "22\n",
      "Card(rank='8', suit='hearts')\n",
      "26\n",
      "Card(rank='9', suit='hearts')\n",
      "30\n",
      "Card(rank='10', suit='hearts')\n",
      "34\n",
      "Card(rank='J', suit='hearts')\n",
      "38\n",
      "Card(rank='Q', suit='hearts')\n",
      "42\n",
      "Card(rank='K', suit='hearts')\n",
      "46\n",
      "Card(rank='A', suit='hearts')\n",
      "50\n",
      "Card(rank='2', suit='clubs')\n",
      "Card(rank='2', suit='diamonds')\n",
      "Card(rank='2', suit='hearts')\n",
      "Card(rank='2', suit='spades')\n",
      "Card(rank='3', suit='clubs')\n",
      "Card(rank='3', suit='diamonds')\n",
      "Card(rank='3', suit='hearts')\n",
      "Card(rank='3', suit='spades')\n",
      "Card(rank='4', suit='clubs')\n",
      "Card(rank='4', suit='diamonds')\n",
      "Card(rank='4', suit='hearts')\n",
      "Card(rank='4', suit='spades')\n",
      "Card(rank='5', suit='clubs')\n",
      "Card(rank='5', suit='diamonds')\n",
      "Card(rank='5', suit='hearts')\n",
      "Card(rank='5', suit='spades')\n",
      "Card(rank='6', suit='clubs')\n",
      "Card(rank='6', suit='diamonds')\n",
      "Card(rank='6', suit='hearts')\n",
      "Card(rank='6', suit='spades')\n",
      "Card(rank='7', suit='clubs')\n",
      "Card(rank='7', suit='diamonds')\n",
      "Card(rank='7', suit='hearts')\n",
      "Card(rank='7', suit='spades')\n",
      "Card(rank='8', suit='clubs')\n",
      "Card(rank='8', suit='diamonds')\n",
      "Card(rank='8', suit='hearts')\n",
      "Card(rank='8', suit='spades')\n",
      "Card(rank='9', suit='clubs')\n",
      "Card(rank='9', suit='diamonds')\n",
      "Card(rank='9', suit='hearts')\n",
      "Card(rank='9', suit='spades')\n",
      "Card(rank='10', suit='clubs')\n",
      "Card(rank='10', suit='diamonds')\n",
      "Card(rank='10', suit='hearts')\n",
      "Card(rank='10', suit='spades')\n",
      "Card(rank='J', suit='clubs')\n",
      "Card(rank='J', suit='diamonds')\n",
      "Card(rank='J', suit='hearts')\n",
      "Card(rank='J', suit='spades')\n",
      "Card(rank='Q', suit='clubs')\n",
      "Card(rank='Q', suit='diamonds')\n",
      "Card(rank='Q', suit='hearts')\n",
      "Card(rank='Q', suit='spades')\n",
      "Card(rank='K', suit='clubs')\n",
      "Card(rank='K', suit='diamonds')\n",
      "Card(rank='K', suit='hearts')\n",
      "Card(rank='K', suit='spades')\n",
      "Card(rank='A', suit='clubs')\n",
      "Card(rank='A', suit='diamonds')\n",
      "Card(rank='A', suit='hearts')\n",
      "Card(rank='A', suit='spades')\n"
     ]
    }
   ],
   "source": [
    "for card in deck :\n",
    "    print(card)\n",
    "    print(spades_high(card))\n",
    "for card in sorted(deck, key=spades_high):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### sorted(iterable, *, key=None, reverse=False)\n",
    "Return a new sorted list from the items in iterable.\n",
    "\n",
    "Has two optional arguments which must be specified as keyword arguments.\n",
    "\n",
    "key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).\n",
    "\n",
    "reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.\n",
    "\n",
    "Use functools.cmp_to_key() to convert an old-style cmp function to a key function.\n",
    "\n",
    "The built-in sorted() function is guaranteed to be stable. A sort is stable if it guarantees not to change the relative order of elements that compare equal — this is helpful for sorting in multiple passes (for example, sort by department, then by salary grade)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How special methods are used\n",
    "The first thing to know about special methods is that they are meant to be called by the\n",
    "Python interpreter, and not by you. You don’t write my_object.__len__(). You write\n",
    "len(my_object) and, if my_object is an instance of a user defined class, then Python\n",
    "calls the __len__ instance method you implemented.\n",
    "\n",
    "Normally, your code should not have many direct calls to special methods. Unless you\n",
    "are doing a lot of metaprogramming, you should be implementing special methods\n",
    "more often than invoking them explicitly. The only special method that is frequently\n",
    "called by user code directly is __init__, to invoke the initializer of the superclass in\n",
    "your own __init__ implementation.\n",
    "### Emulating numeric types"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from math import hypot\n",
    "class Vector:\n",
    "    def __init__(self, x=0, y=0):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "    def __repr__(self):\n",
    "        return 'Vector(%r, %r)' % (self.x, self.y)\n",
    "    def __abs__(self):\n",
    "        return hypot(self.x, self.y)\n",
    "    def __bool__(self):\n",
    "        return bool(abs(self))\n",
    "    def __add__(self, other):\n",
    "        x = self.x + other.x\n",
    "        y = self.y + other.y\n",
    "        return Vector(x, y)\n",
    "    def __mul__(self, scalar):\n",
    "        return Vector(self.x * scalar, self.y * scalar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vector(4, 5)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "12.806248474865697"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1=Vector(2,4)\n",
    "v2=Vector(2,1)\n",
    "v=v1+v2\n",
    "print(v)\n",
    "abs(v*2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### String representation\n",
    "The __repr__ special method is called by the repr built-in to get string representation\n",
    "of the object for inspection. If we did not implement __repr__, vector instances would\n",
    "be shown in the console like <Vector object at 0x10e100070>.\n",
    "\n",
    "Contrast __repr__ with with __str__, which is called by the str() constructor and\n",
    "implicitly used by the print function. __str__ should return a string suitable for display\n",
    "to end-users.\n",
    "If you only implement one of these special methods, choose __repr__, because when\n",
    "no custom __str__ is available, Python will call __repr__ as a fallback."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Arithmetic operators\n",
    "### Boolean value of a custom type\n",
    "Python applies bool(x), which always\n",
    "returns True or False.\n",
    "By default, instances of user-defined classes are considered truthy, unless either\n",
    "__bool__ or __len__ is implemented. Basically, bool(x) calls x.__bool__() and uses\n",
    "the result. If __bool__ is not implemented, Python tries to invoke x.__len__(), and if\n",
    "that returns zero, bool returns False. Otherwise bool returns True.\n",
    "## Overview of special methods\n",
    "### Table 1-1. Special method names (operators excluded).\n",
    "category method names\n",
    "string/bytes representation __repr__, __str__, __format__, __bytes__\n",
    "conversion to number __abs__, __bool__, __complex__, __int__, __float__, __hash__, __in\n",
    "dex__\n",
    "emulating collections __len__, __getitem__, __setitem__, __delitem__, __contains__\n",
    "iteration __iter__, __reversed__, __next__\n",
    "emulating callables __call__\n",
    "context management __enter__, __exit__\n",
    "instance creation and destruction __new__, __init__, __del__\n",
    "attribute management __getattr__, __getattribute__, __setattr__, __delattr__, __dir__\n",
    "attribute descriptors __get__, __set__, __delete__\n",
    "class services __prepare__, __instancecheck__, __subclasscheck__\n",
    "\n",
    "### Table 1-2. Special method names for operators.\n",
    "category method names and related operators\n",
    "unary numeric operators __neg__ -, __pos__ +, __abs__ abs()\n",
    "rich compartison operators __lt__ >, __le__ <=, __eq__ ==, __ne__ !=, __gt__ >, __ge__ >=\n",
    "arithmetic operators __add__ +, __sub__ -, __mul__ *, __truediv__ /, __floordiv__ //, __mod__%, __divmod__ divmod() , __pow__ ** or pow(), __round__ round()\n",
    "reversed arithmetic operators __radd__, __rsub__, __rmul__, __rtruediv__, __rfloordiv__,__rmod__, __rdivmod__, __rpow__\n",
    "augmented assignment arithmetic operators __iadd__, __isub__, __imul__, __itruediv__, __ifloordiv__, __imod__, __ipow__\n",
    "bitwise operators  __invert__ ~, __lshift__ <<, __rshift__ >>, __and__ &, __or__ |,__xor__ ^\n",
    "reversed bitwise operators __rlshift__, __rrshift__, __rand__, __rxor__, __ror__\n",
    "augmented assignment bitwise operators __ilshift__, __irshift__, __iand__, __ixor__, __ior__\n",
    "\n",
    "## Why len is not a method\n",
    "In other words, len is not called as a method because it gets special treatment as part of\n",
    "the Python Data Model, just like abs. But thanks to the special method __len__ you\n",
    "can also make len work with your own custom objects. This is fair compromise between\n",
    "the need for efficient built-in objects and the consistency of the language. Also from the\n",
    "Zen of Python: “Special cases aren’t special enough to break the rules.”"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
